#ifndef __CCMS_NR_PROCESS_H__
#define __CCMS_NR_PROCESS_H__

/**
 * @file ccms/nr/process.h
 *
 * Process class header file.
 */

#include "ccms/nr/exception.h"
#include <sys/types.h>
#include <string>

namespace ccms {
namespace nr {

/**
 * Process class, holds the information about the state of a process.
 */
class Process {

    public:
        /**
         * Default constructor.
         */
        Process(void);

        /**
         * Initializes the object with data provided as arguments.
         * @param pid Process ID (PIDi) of this process
         * @throw InvalidParameterException if an invalid pid is passed
         */
        Process(const pid_t pid);

        /**
         * Updates the information about the state of the process.
         * The update is based on the information read from /proc/\<PID\>/stat.
         * and /proc/\<PID\>/smaps.
         * @throw Exception on update failure
         */
        void update(void);

        /**
         * Returns the process ID (PID) of this process.
         * @return the process ID (PID) of this process
         */
        pid_t getPid(void) const;

        /**
         * Returns the user ID (UID) of the owner of the process.
         * @return the user ID (UID) of the owner of the process
         */
        uid_t getOwnerUid(void) const;

        /**
         * Returns the command name of this process.
         * @return the command name of this process
         */
        const std::string& getCommandName(void) const;

        /**
         * Returns the difference between the user time (utime) values 
         * read from /proc/PID/stat in the last two subsequent update calls.
         * @return the most recent value of utime for this process
         */
        unsigned long getUserTimeDiff(void) const;

        /**
         * Returns the difference between the system time (stime) values 
         * read from /proc/PID/stat in the last two subsequent update calls.
         * @return the most recent value of utime for this process
         */
        unsigned long getSystemTimeDiff(void) const;

        /**
         * Returns the value of the Resident Set Size read from /proc/PID/stat,
         * in pages.
         * @return the rss, in pages
         */
        long getRss(void) const;

        /**
         * Returns the value of the Proportional Set Size read from /proc/PID/smaps,
         * in kilobytes.
         * @return the pss, in kilobytes
         */
        long getPss(void) const;

        /**
         * Returns boolean value indicating whether the process still exists
         * in the /proc tree.
         * @return true if the process still exists in the /proc tree
         * @return false if the process does not already exist in the /proc tree
         */
        bool getExists(void) const;

        /**
         * Sets the 'exists' flag
         */
        void setExists(const bool exists);

    private:
        /**
         * Updates statistics from /proc/<PID>/stat.
         */
        void updateFromStat(void);

        /**
         * Updates statistics from /proc/<PID>/smaps.
         */
        void updateFromSmaps(void);

        /**
         * Normalizes the difference between last two utime/stime values read
         * from /proc/<PID>/stat.
         *
         * This is needed due to a bug in Linux Kernel, that makes subsequent
         * utime/stime values not grow monotonically (the most recent value 
         * may be lower than the previous one).
         *
         * See http://lkml.org/lkml/2008/8/30/8 for more details.
         *
         * @return the difference between \a rec and \a prev if \a rec >= \a
         * prev, 0 is returned otherwise
         */
        static unsigned long normalize(unsigned long rec, unsigned long prev);


        /**
         * PID of the process.
         */
        pid_t pid;

        /**
         * Command name of the process.
         */
        std::string commandName;

        /**
         * User time (utime) read from /proc/PID/stat, the most recent value.
         */
        unsigned long userTimeRec;

        /**
         * User time (utime) read from /proc/PID/stat, the previous value.
         */
        unsigned long userTimePrev;

        /**
         * System time (stime) read from /proc/PID/stat, the most recent value.
         */
        unsigned long systemTimeRec;

        /**
         * System time (stime) read from /proc/PID/stat, the previous value.
         */
        unsigned long systemTimePrev;

        /**
         * Resident Set Size, in pages.
         *
         * Deprecated due to it's nature (it fully accounts shared memory,
         * in contrast to Proportional Set Size, where shared memory is
         * accounted proportionally to it's use by all processes in the system.
         */
        long rss;

        /**
         * Proportional Set Size, in kilobytes.
         */
        long pss;

        /**
         * Indicates whether the process still exists in the /proc/ tree.
         */
        bool exists;
};

inline pid_t Process::getPid(void) const
{
    return pid;
}

inline const std::string& Process::getCommandName(void) const
{
    return commandName;
}

inline unsigned long Process::getUserTimeDiff(void) const
{
    return Process::normalize(userTimeRec, userTimePrev);
}

inline unsigned long Process::getSystemTimeDiff(void) const
{
    return Process::normalize(systemTimeRec, systemTimePrev);
}

inline long Process::getRss(void) const
{
    return rss;
}

inline long Process::getPss(void) const
{
    return pss;
}

inline bool Process::getExists(void) const
{
    return exists;
}

inline void Process::setExists(const bool exists)
{
    this->exists = exists;
}

inline unsigned long Process::normalize(const unsigned long rec, const unsigned long prev) {
    return (rec >= prev) ? (rec - prev) : 0;
}

} // namespace nr
} // namespace ccms

#endif /* __CCMS_NR_PROCESS_H__ */
